package org.alex.samples.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public abstract class BaseHibernateDao<T> implements IBaseDao<T> {

	private Class<T> entityClass;

	@Autowired
	private SessionFactory sessionFactory;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public BaseHibernateDao() {
		this.entityClass = null;
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.entityClass = (Class<T>) p[0];
		}
	}

	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

	public void flush() {
		this.getSession().flush();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T get(Serializable id) {
		return (T) this.getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T getWithLock(Serializable id, LockOptions lockOptions) {
		T t = (T) this.getSession().get(entityClass, id, lockOptions);
		if (t != null) {
			this.flush();
		}
		return t;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T load(Serializable id) {
		String hql = "FROM" + entityClass.getName();
		return (T) queryForList(hql, null);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T LoadWithLock(Serializable id, LockOptions lockOptions) {
		T t = (T) this.getSession().load(entityClass, id, lockOptions);
		if (t != null) {
			this.flush();
		}
		return t;
	}

	@Override
	public List<T> loadAll() {
		String hql = "FROM " + entityClass.getName();
		return queryForList(hql, null);
	}

	@Override
	public void update(T entity) {
		if (null == entity) {
			return;
		}
		this.getSession().update(entity);
	}

	@Override
	public void save(T entity) {
		if (null == entity) {
			return;
		}
		this.getSession().save(entity);
	}

	@Override
	public void saveOfUpdate(T entity) {
		if (null == entity) {
			return;
		}
		this.getSession().saveOrUpdate(entity);
	}

	@Override
	public void save(Collection<T> entities) {
		if (null == entities) {
			return;
		}
		for (T t : entities) {
			this.getSession().save(t);
		}
	}

	@Override
	public void delete(T entity) {
		this.getSession().delete(entity);
	}

	@Override
	public void deleteByKey(Serializable id) {
		this.getSession().delete(this.load(id));
	}

	@Override
	public void deleteAll(Collection<T> entities) {
		if (null == entities) {
			return;
		}
		for (T t : entities) {
			this.getSession().delete(t);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(String queryString) {
		Query query = this.getSession().createQuery(queryString);
		return query.list();
	}

	@Override
	public List<T> find(String queryString, Object[] objects) {
		return this.queryForList(queryString, objects);
	}

	@Override
	public Iterator<T> iterate(String queryString) {
		return this.find(queryString).iterator();
	}

	@Override
	public Iterator<T> iterate(String queryString, Object[] objects) {
		return this.find(queryString, objects).iterator();
	}

	@Override
	public void closeIterator(Iterator<T> iterator) {
		this.closeIterator(iterator);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T queryForObject(final String hql, final Object[] params) {
		return (T) createQuery(hql, params).uniqueResult();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T queryForTopObject(String hql, Object[] objects) {
		return (T) createQuery(hql, objects).setFirstResult(0).setMaxResults(1).uniqueResult();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> queryForList(final String hql, final Object[] objects, final int recordNum) {
		return createQuery(hql, objects).setFirstResult(0).setMaxResults(recordNum).list();
	}

	@Override
	public DetachedCriteria createDetachedCriteria() {
		return DetachedCriteria.forClass(this.entityClass);
	}

	@Override
	public Criteria createCriteria() {
		return this.createDetachedCriteria().getExecutableCriteria(getSession());
	}

	@Override
	public Query createQuery(String queryString) {
		return this.getSession().createQuery(queryString);
	}

	@Override
	public Query createQuery(final String hql, final Object[] params) {
		Query query = this.createQuery(hql);
		setQueryParams(query, params);
		return query;
	}

	@Override
	public SQLQuery createSqlQuery(String queryString) {
		return this.getSession().createSQLQuery(queryString);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> queryForList(final String hql, final Object[] objects) {
		return createQuery(hql, objects).list();
	}

	public void setQueryParams(Query query, Object[] params) {
		if (null == params) {
			return ;
		}
		for(int i = 0; i < params.length; i++) {
			query.setParameter(i, params[i]);
		}
	}
}
